
/**
 * @param {Function} fn the function to call in the next turn of the event loop
 * @returns {number} the timeout id for the deferred function call
 */
export function defer(fn) {
    return setTimeout(fn, 1);
}

/**
 * @param {Function} fn the function to choke
 * @param {number} [wait] time to wait in millis (default 1 ms)
 * @returns {Function} a proxy to fn that delays invocation, and discards subsequent invocations, until the specified
 *          wait time has elapsed.
 */
export function choke(fn, wait = 1) {
    let task = 0;
    return function() {
        if (task === 0) {
            const args = [...arguments];
            task = setTimeout(() => { task = 0, fn.apply(this, args) }, wait);
        }
    };
}

/**
 * @param {Function} fn
 * @param {number?} wait millis to wait before invocation. If not specified, fn is invoked immediately.
 * @returns {number|undefined} the result of setTimeout or undefined when invoked immediately
 */
export function invoke(fn, wait) {
    return wait !== undefined ? setTimeout(fn, wait) : (void fn());
}
